﻿// <copyright file="RangeAreaDataView.cs" company="Oleg V. Polikarpotchkin">
// Copyright © 2008-2009 Oleg V. Polikarpotchkin. All Right Reserved
// </copyright>
// <author>Oleg V. Polikarpotchkin</author>
// <email>ov-p@yandex.ru</email>
// <date>2009-05-26</date>
// <summary>OpenWPFChart library. RangeAreaDataView describes the data to paint the area between two Curves.</summary>
// <revision>$Id$</revision>

using System;
using System.Collections;
using System.ComponentModel;
using System.Windows.Media;

namespace OpenWPFChart.Parts
{
	/// <summary>
	/// <see cref="RangeAreaDataView"/> describes the data to paint the area between two Curves.
	/// </summary>
	/// <remarks>
	/// <see cref="RangeAreaDataView"/> holds the pair of <see cref="ItemDataView"/> Curve 
	/// objects and a brush to paint the area between these Curves.
	/// </remarks>
	public class RangeAreaDataView : ItemsHolderView
	{
		/// <summary>
		/// Initializes a new instance of the <see cref="RangeAreaDataView"/> class.
		/// </summary>
		/// <remarks>
		/// This object default VisualCue is the <see cref="RangeAreaVisual"/> Type.
		/// </remarks>
		public RangeAreaDataView()
		{
			VisualCue = typeof(RangeAreaVisual);
		}

		/// <inheritdoc />
		public override void Replace(ItemDataView itemToReplace, ItemDataView replacement)
		{
			if (itemToReplace == ItemDataView1)
				ItemDataView1 = replacement;
			else if (itemToReplace == ItemDataView2)
				ItemDataView2 = replacement;
		}

		#region Pair of ItemDataView objects
		#region ItemDataView1
		ItemDataView itemDataView1;
		/// <summary>
		/// Gets or sets the ItemDataView1 property.
		/// </summary>
		/// <value>ItemDataView object</value>
		public ItemDataView ItemDataView1
		{
			get { return isItemDataViewMappedToCurveVisual(itemDataView1) ? itemDataView1 : null; }
			set
			{
				if (itemDataView1 != value)
				{
					if (itemDataView1 != null)
						itemDataView1.PropertyChanged -= ItemDataViewChanged;

					itemDataView1 = value;

					if (itemDataView1 != null)
						itemDataView1.PropertyChanged += ItemDataViewChanged;

					NotifyPropertyChanged("ItemDataView1");
				}
			}
		}
		#endregion ItemDataView1

		#region ItemDataView2
		ItemDataView itemDataView2;
		/// <summary>
		/// Gets or sets the ItemDataView2 property.
		/// </summary>
		/// <value>ItemDataView object</value>
		public ItemDataView ItemDataView2
		{
			get { return isItemDataViewMappedToCurveVisual(itemDataView2) ? itemDataView2 : null; }
			set
			{
				if (itemDataView2 != value)
				{
					if (itemDataView2 != null)
						itemDataView2.PropertyChanged -= ItemDataViewChanged;

					itemDataView2 = value;

					if (itemDataView2 != null)
						itemDataView2.PropertyChanged += ItemDataViewChanged;

					NotifyPropertyChanged("ItemDataView2");
				}
			}
		}
		#endregion ItemDataView2

		/// <summary>
		/// ItemDataView PropertyChanged event handler.
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.ComponentModel.PropertyChangedEventArgs"/> instance containing the event data.</param>
		void ItemDataViewChanged(object sender, PropertyChangedEventArgs e)
		{
			if (sender.Equals(itemDataView1))
				NotifyPropertyChanged("ItemDataView1." + e.PropertyName);
			else if (sender.Equals(itemDataView2))
				NotifyPropertyChanged("ItemDataView2." + e.PropertyName);
		}

		/// <summary>
		/// Determines whether the <paramref name="itemDataView"/> is mapped to curve visual.
		/// </summary>
		/// <param name="itemDataView">The item data view.</param>
		/// <returns>
		/// 	<c>true</c> if the <paramref name="itemDataView"/> is mapped to curve visual; 
		/// 	otherwise, <c>false</c>.
		/// </returns>
		static bool isItemDataViewMappedToCurveVisual(ItemDataView itemDataView)
		{
			if (itemDataView == null)
				return false;
			Type visualType = itemDataView.VisualCue as Type;
			if (visualType != null)
			{
				if (visualType.GetInterface("ICurveVisual") != null)
					return true;
			}
			else if (itemDataView.VisualCue is IEnumerable)
			{
				foreach (object item in itemDataView.VisualCue as IEnumerable)
				{
					visualType = item as Type;
					if (visualType != null)
					{
						if (visualType.GetInterface("ICurveVisual") != null)
							return true;
					}
				}
			}
			return false;
		}
		#endregion Pair of ItemDataView objects

		#region AreaBrush
		Brush areaBrush;
		/// <summary>
		/// Gets or sets the <see cref="AreaBrush"/> property.
		/// </summary>
		/// <value>Any <see cref="System.Windows.Media.Brush"/> or <c>null</c> (the default).</value>
		/// <remarks>
		/// This <see cref="System.Windows.Media.Brush"/> is used to fill the area between the two
		/// Curve lines. If the <c>AreaBrush</c> is <c>null</c> the area isn't filled.
		/// </remarks>
		public Brush AreaBrush
		{
			get { return areaBrush; }
			set
			{
				if (areaBrush != value)
				{
					if (areaBrush != null && !areaBrush.IsFrozen)
						areaBrush.Changed -= AreaBrushChanged;
					areaBrush = value;
					if (areaBrush != null && !areaBrush.IsFrozen)
						areaBrush.Changed += AreaBrushChanged;
					NotifyPropertyChanged("AreaBrush");
				}
			}
		}

		/// <summary>
		/// AreaBrush changed.
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		void AreaBrushChanged(object sender, EventArgs e)
		{
			NotifyPropertyChanged("AreaBrush");
		}
		#endregion AreaBrush

		#region ICloneable Override
		/// <summary>
		/// Creates a new object that is a copy of the current instance.
		/// </summary>
		/// <returns>
		/// A new object that is a copy of this instance.
		/// </returns>
		/// <remarks>This method is designed to provide the deep cloning facility if desired.</remarks>
		protected override object CloneInternal()
		{
			RangeAreaDataView clone = base.CloneInternal() as RangeAreaDataView;

			// Pair of ItemDataView objects should not be cloned!

			// AreaBrush.
			if (areaBrush != null)
			{
				clone.areaBrush = areaBrush.Clone();
				clone.areaBrush.Changed += clone.AreaBrushChanged;
			}
			return clone;
		}
		#endregion ICloneable Override
	}
}
